""" This module contains the definitions of static part of pi-graphs
"""
from action import GuardedAction, Input, Output, Silence
from com.pigparser import PigParser, PigLexer
from name import FreeName, GResName, Guard, Match, Mismatch, TResName, VarName
from com.pigdom import *

class Replicator:
    def __init__(self, name, bound, tresnames, varnames, actions, successors):
        self._name = name
        self._bound = bound
        self._tresnames = tresnames
        self._varnames = varnames
        self._actions = actions
        self._successors = successors
        
    def name(self):
        return self._name
    
    def bound(self):
        return self._bound
    
    def tresNames(self):
        return self._tresnames
    
    def varNames(self):
        return self._varnames
    
    def node(self, k=None):
        if k is None:
            return self._actions.keys()
        else:
            return self._actions[k]
    
    def initialNodes(self):
        U = set(self._actions.keys())
        for n in self._successors.keys():
            U = U - self._successors[n]
        return U
    
    def finalNodes(self):
        N1 = set(self._actions.keys())
        N2 = set(self._successors.keys())
        return N1 - N2
    
    def succ(self, k=None):
        if k is None:
            return self._successors
        elif k not in self._successors:
            return set([])
        else:
            return self._successors[k]
            
    def check(self):
        """ Check syntactic constrains
        """
        pass
    

class PiGraph:
    def __init__(self, gresnames, freenames, replicators):
        self._gresnames = gresnames
        self._freenames = freenames
        self._replicators = replicators
        
    def gresNames(self):
        return self._gresnames
    
    def freeNames(self):
        return self._freenames
    
    def replicator(self, rname=None):
        if rname is None:
            return self._replicators.values()
        else:
            return self._replicators[rname]
    
    def rnames(self):
        return self._replicators.keys()
    
    
class PiGraphBuilder:
    def __init__(self):
        self._parser = PigParser
        self._converter = PigConverter()
        
    def inputSpec(self, s):
        '''Input a pi-graph specification to create a pi-graph.'''
        # translate the specification into a DOM
        ast = self._parser.parse(s, lexer=PigLexer)
        self._dom = self._converter.toDOM(ast)
#         print self._dom.toprettyxml(indent="    ")
        # Collect names and create name objects
        self.collectNames()
        
        self._counter = 0  # used for generating node ID
        self._R = {}  # R[r]=[name, bound, actions, successor]
        rnodes = self._dom.getElementsByTagName(TAG_DIA_BODY)[0].childNodes
        for rnode in rnodes:
            rname = rnode.getAttribute(ATTR_NAME)
            bound = rnode.getAttribute(ATTR_BOUND)
            self.addReplicator(rname, int(bound))
            pnodes = rnode.getElementsByTagName(TAG_REPL_BODY)[0].childNodes
            for pnode in pnodes:
                self.doProcessNode(pnode, rname, None)
        
    def addReplicator(self, rname, bound):
        """ Add a replicator into graph.
        """
        if rname in self._R:
            raise Exception("replicator existed")
        self._R[rname] = [bound, {}, {}]  # actions, successor
        
    def addAction(self, rname, guard, action):
        """ Add an action into a replicator
        """
        if rname not in self._R.keys():
            raise Exception("replicator not found")
        self._counter += 1
        actions = self._R[rname][1]
        actions[self._counter] = GuardedAction(guard, action)
        return self._counter
    
    def connect(self, rname, source, target):
        """ Assign the precedence
        """
        if rname not in self._R.keys():
            raise Exception("replicator not found")
        successor = self._R[rname][2]
        if source in successor:
            successor[source].add(target)
        else:
            successor[source] = set([target])

    def doProcessNode(self, pnode, rname, parent=None):
        '''Process a 'process' node recursively.'''
        guard = self.collectGuard(pnode, rname)
        action = self.collectAction(pnode, rname)
        k = self.addAction(rname, guard, action)
        if parent is not None:
            self.connect(rname, parent, k)
        C = pnode.getElementsByTagName(TAG_CHILDREN)[0].childNodes
        for c in C:
            self.doProcessNode(c, rname, k)
                
    def collectGuard(self, pnode, rname):  # fix the way to construct a guard
        '''Collect a Guard() from a given 'process' node.'''
        guard = Guard()
        for cnode in pnode.firstChild.childNodes:
            kind = cnode.getAttribute(ATTR_KIND)
            name1 = cnode.getAttribute(ATTR_COMP_NAME_1)
            name2 = cnode.getAttribute(ATTR_COMP_NAME_2)
            name1_obj = self.nameObject(name1, rname)
            name2_obj = self.nameObject(name2, rname)
            if kind == ATTR_COMP_MACTH:  # a match
                guard.add(Match(name1_obj, name2_obj))
            else:  # mismatch
                guard.add(Mismatch(name1_obj, name2_obj))
        return guard
    
    def collectAction(self, pnode, rname):
        '''Collect an Action() from a given 'process' node.'''
        anode = pnode.firstChild.nextSibling  # action node
        kind = anode.getAttribute(ATTR_KIND)
        if kind == ATTR_ACT_TAU:
            return Silence()
        else:
            chan = self.nameObject(anode.getAttribute(ATTR_ACT_CHANNEL), rname)
            name = self.nameObject(anode.getAttribute(ATTR_NAME), rname)
            if kind == ATTR_ACT_INP:
                return Input(chan, name)
            else:
                return Output(chan, name)
                
    def collectNames(self):
        '''Collect all names and create instances.'''
        # reset name space
        self._gres = {}  # name -> object
        self._free = {}  # name -> object
        self._tres = {}  # r-name -> {name -> object}
        self._vars = {}  #  r-name -> {name -> object}
        
        self.collectGResNames()
        rnodes = self._dom.getElementsByTagName(TAG_DIA_BODY)[0].childNodes
        for rnode in rnodes:
            self.collectNamesInReplicator(rnode)
    
    def nameObject(self, name, rname):
        '''Get an object of a given name.'''
        if name in self._vars[rname]:
            return self._vars[rname][name]
        if name in self._tres[rname]:
            return self._tres[rname][name]
        if name in self._gres:
            return self._gres[name]
        if name in self._free:
            return self._free[name]
        raise ValueError("name not found")

    def collectGResNames(self):
        names = []
        N = self._dom.getElementsByTagName(TAG_DIA_DECL)[0].childNodes
        for n in N:
            names.append(n.getAttribute(ATTR_NAME))
        for name in names:
            self._gres[name] = GResName(name)
            
    def collectNamesInReplicator(self, rnode):
        rname = rnode.getAttribute(ATTR_NAME)
        #
        self._tres[rname] = {}
        names = set()
        for n in rnode.getElementsByTagName(TAG_NAME):
            names.add(n.getAttribute(ATTR_NAME))
        for name in names:
            self._tres[rname][name] = TResName(name, rname)
        # 
        self._vars[rname] = {}
        names = set()
        for a in rnode.getElementsByTagName(TAG_ACTION):
            if a.getAttribute(ATTR_KIND) == ATTR_ACT_INP:
                name = a.getAttribute(ATTR_NAME)
                if (name not in self._tres[rname]
                    and name not in self._gres):
                    names.add(name)
        for name in names:
            self._vars[rname][name] = VarName(name, rname)
        #
        names = set()
        for a in rnode.getElementsByTagName(TAG_ACTION):
            kind = a.getAttribute(ATTR_KIND)
            if kind == ATTR_ACT_INP:
                names.add(a.getAttribute(ATTR_ACT_CHANNEL))
            elif kind == ATTR_ACT_OUT:
                names.add(a.getAttribute(ATTR_ACT_CHANNEL))
                names.add(a.getAttribute(ATTR_NAME))
        for c in rnode.getElementsByTagName(TAG_COMPARISON):
            names.add(c.getAttribute(ATTR_COMP_NAME_1))
            names.add(c.getAttribute(ATTR_COMP_NAME_2))
        for name in names:
            if (name not in self._vars[rname] 
                and name not in self._tres[rname] 
                and name not in self._gres):
                self._free[name] = FreeName(name)
                
    def build(self):
        """ Build a pi-graph and return it
        """
        gresnames = self._gres.values()
        freenames = self._free.values()
        repls = {}
        for rname in self._R:
            tresnames = self._tres[rname].values()
            varnames = self._vars[rname].values()
            bound = self._R[rname][0]
            actions = self._R[rname][1]
            successor = self._R[rname][2]
            r = Replicator(rname, bound, tresnames, varnames, actions, successor)
            repls[rname] = r
            r.check()
        return PiGraph(gresnames, freenames, repls)
